#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glob.h>
#include <time.h>
#include <string.h>
#include "bss.h"
#include "glib_port.h"

#define MAX_CUTOFF_LEN 7
#define MAX_ADDITIONAL_LEN 100
#define MAX_SUFFIX_LEN 15

//enum search_types{SEQ2BES, MRK2BES, MRK2SEQ} search_type=SEQ2BES;
//enum search_space_types{ALL_CTGS, CTG_ENDS, SINGLE_CTG} search_space_type=ALL_CTGS;
//enum search_tool_types search_tool_type=MEGABLAST;

struct displayedFile *displayed_files;
#define FSCANLINE(x) fscanf(fp,"%1023[^\n]\n",x)
#define GETCURDIR (strcmp(dirName,"") ? dirName : ".")
#define MAX_DEPTH 10 // maximum depth to descend in diretory structure

#ifdef MAC_BUILD
#define GLOB_NOMATCH 0
#endif

extern void show_help();
extern int compare_strings();
extern void do_search(parsetype,int,int);
extern void display_result_file();
extern void kill_all_children();
extern BOOL find_Clone();
extern void close_results();
extern void display_filter();
extern int add_bss_hits_to_fpc();
extern void markersinctgcount();
extern void sortoutmarkersman();
extern void refreshlist();
extern void updateproj();
void close_filter_window(GtkWidget *widget, gpointer data);
extern void close_results();
int set_file_vars();

extern char blastoutfile[];
extern int use_blast_flag;
extern int use_blat_flag;
extern int use_megablast_flag;
extern int split_bss_flag;
extern long double eVal;
extern int blatscore;
extern int clones_matched;
extern int clones_not_matched;
extern char qryDir[];
extern char qryFile[];
extern char qrySuffix[];
extern char dbDir[];
extern char dbFile[];
extern char dbSuffix[];
extern char xtraParm[];
extern GtkWidget *ctg_window;
extern int save_output_flag;
extern int new_marker_cnt, electronic_cnt, econfirmed_cnt;

GtkWidget *bss_window=NULL;
GtkWidget *ctgends_entry;
GtkWidget *singlectg_entry;
GtkWidget *cutoff_entry;
GtkWidget *cutoff_label;
GtkWidget *disp_files_clist;
GtkWidget *additional_entry;

GtkWidget *query_path_entry;
char query_path[MAXPATHLEN+1]="";

GtkWidget *db_path_entry;
char db_path[MAXPATHLEN+1]="";

char cutoff_str[MAX_CUTOFF_LEN+1];
char additional_blast[MAX_ADDITIONAL_LEN+1] = "-F F";
char additional_megablast[MAX_ADDITIONAL_LEN+1] = "-F F";
char additional_blat[MAX_ADDITIONAL_LEN+1] = "";
char show_file_glob[MAX_SUFFIX_LEN+1]="*";
char new_file_suffix[MAX_SUFFIX_LEN+1]="bss";

int max_displayed_files=0;
int num_displayed_files=0;
int bss_files_selected_row;

int blat_score=100;
long double blast_eval=1e-100;
int singlectg=1;

typedef struct
{
    gchar* bss_path;
    gchar* new_bss_subdir;
} ModuleCallbackData;

static ModuleCallbackData cbdata = {NULL, NULL};

typedef struct
{
    GtkFileSelection* selector;
    ModuleCallbackData* cbdata;
} StoreQueryPathData;

static StoreQueryPathData sqpdata;

static void start_search_callback(GtkWidget *widget, gpointer data);
static void delete_selected_file();
static void delete_displayed_files();
static void get_bss_results(const gchar* bss_path);
static void get_results(const gchar* dirname, const gchar* top);
static void get_results_cwd(const gchar* path, const gchar* top);
static GtkWidget* bss_options_frame(GtkWidget* split_bss_button);
static GtkWidget* labelled_entry(const gchar* text, guint max_length,
                                 GtkSignalFunc cb);
static void cancel_button_callback(GtkWidget *widget, gpointer data);

/* The items for the menu bar at the top of the ctgdisplay*/
static GtkItemFactoryEntry menu_items[] = {
  { "/File", NULL, NULL, 0, "<Branch>" },
  { "/File/Delete selected results file", "<control>D", delete_selected_file, 0, NULL },
  { "/File/Delete all results files", NULL, delete_displayed_files, 0, NULL },
  { "/File/Close", NULL, cancel_button_callback, 0, NULL},
};

static GtkItemFactory *item_factory;


static void
get_main_menu( GtkWidget  *window,
               GtkWidget **menubar,
               ModuleCallbackData* data)
{
  GtkAccelGroup *accel_group;
  gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);

  accel_group = gtk_accel_group_new ();

  item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
                                       accel_group);

  gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, data);

  gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

  if (menubar)
    *menubar = gtk_item_factory_get_widget (item_factory, "<main>");
}

static void
cancel_button_callback(GtkWidget *widget, gpointer data)
{
  close_filter_window(0,0);
  close_results(0,0);
  if(bss_window!=NULL)
    gtk_widget_destroy(bss_window);
  bss_window=NULL;
}

static void options_callback(GtkWidget *widget, gpointer data)
{
  switch(search_tool_type){
    case BLAST:
      system("blastall");
      break;
    case MEGABLAST:
      system("blastall");
      break;
    case BLAT:
      system("blat");
      break;
    default:
      g_warning("Invalid search tool");
  }
}



static void
get_displayed_files_mem(){
  if(num_displayed_files >= max_displayed_files-1){
    if(max_displayed_files==0){
      max_displayed_files=100;
      displayed_files=calloc(max_displayed_files,sizeof(struct displayedFile));
      NOMEM2(displayed_files,"displayed_files");
    }
    else{
      max_displayed_files+=500;
      displayed_files=realloc(displayed_files,sizeof(struct displayedFile)*(max_displayed_files));
      NOMEM2(displayed_files,"displayed_files");
    }
  }
}

static void
add_to_displayed_files(char *name, char *date, off_t size){
  if(num_displayed_files >= max_displayed_files) get_displayed_files_mem();
  strcpy(displayed_files[num_displayed_files].name,name);
  displayed_files[num_displayed_files].size = size;
  strcpy(displayed_files[num_displayed_files++].date,date);
}

// get_results
//
// Get the bss results in directory 'dirname', which is specified
// relative to the top-level directory 'top'.
//
static void
get_results(const gchar* dirname, const gchar* top)
{
    struct stat s;
    gchar buff[BUFSIZ/4];
    int here;
    gchar* dir;

    dir = g_strconcat(top, dirname, NULL);
    if (stat(dir, &s) == 0 && S_ISDIR(s.st_mode)) {
        here = open(".", O_RDONLY);
        if (here >= 0) {
            if (chdir(dir) == 0){
                get_results_cwd(dir, top);
                fchdir(here);
            }
            else {
                strerror_r(errno, buff, sizeof(buff));
                g_warning("Failed to change directory to %s: %s", dirname,
                          buff);
            }
            close(here);
        }
        else {
            strerror_r(errno, buff, sizeof(buff));
            g_warning("Failed to open current directory: %s", buff);
        }
    }
//    else {
//        g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
//              "Directory %s does not exist or has wrong permissions", dir);
//    }
    g_free(dir);
}

// get_results_cwd
//
// Get bss results in current working directory. 'path' specifies the
// current directory, and 'top' specifies the query directory. Note
// that 'path' and 'top' are relative to the same directory here.
//
static void
get_results_cwd(const gchar* path, const gchar* top)
{
    gchar buff[BUFSIZ/4];
    int here;
    int ret;
    const gchar summary[] = "summary";

    here = open(".", O_RDONLY);
    if (here >= 0) {
        glob_t globbuf;
        gchar* file_glob;
        if (strlen(show_file_glob) > 0)
            file_glob = g_strdup(show_file_glob);
        else
            file_glob = g_strdup("*");
        if (strcmp(path, top) != 0) {
            gchar* bss_path = g_strconcat(path, "../", NULL);
            add_to_displayed_files(bss_path + strlen(top), "", 0);
            g_free(bss_path);
        }
        ret = glob(file_glob, GLOB_ERR | GLOB_MARK, NULL, &globbuf);
        if (ret == GLOB_NOMATCH) {
            g_free(file_glob);
            g_assert(strlen(show_file_glob) > 0);
            file_glob = g_strdup_printf("*%s*", show_file_glob);
            ret = glob(file_glob, GLOB_ERR | GLOB_MARK, NULL, &globbuf);
        }
        if (ret == 0) {
            gchar** entry;
            entry = globbuf.gl_pathv;
            while (entry != NULL && *entry != NULL) {
                gchar* bss_path;
                gchar tbuff[26];
                struct stat s;
                bss_path = g_strconcat(path, *entry, NULL);
                if ((*entry)[strlen(*entry) - 1] == '/') {
                    // directory
                    if (stat(*entry, &s) == 0) {
                        ctime_r(&s.st_mtime, tbuff);  // SOLARIS  ctime_r(&s.st_mtime, tbuff,26)
                        tbuff[strlen(tbuff) - 1] = '\0';
                        add_to_displayed_files(bss_path + strlen(top),
                                               tbuff, 0);
                    }
                }
                else if (strncmp(*entry, summary,
                                 sizeof(summary) - 1) != 0) {
                    // contig file
                    FILE* f;
                    if (stat(*entry, &s) == 0 &&
                        (f = fopen(*entry, "r")) != NULL) {
                        if (fgets(buff, sizeof(buff), f) == buff &&
                            strstr(buff, "BSS") != NULL) {
                            ctime_r(&s.st_mtime, tbuff); // SOLARIS ctime_r(&s.st_mtime, tbuff,26);
                            tbuff[strlen(tbuff) - 1] = '\0';
                            add_to_displayed_files(bss_path + strlen(top),
                                                   tbuff, s.st_size);
                        }
                        fclose(f);
                    }
                    else {
                        strerror_r(errno, buff, sizeof(buff));
                        g_warning("Failed to open file %s: %s",
                                  bss_path, buff);
                    }
                }
                g_free(bss_path);
                ++entry;
            }
        }
        globfree(&globbuf);
        g_free(file_glob);
        close(here);
    }
    else {
        strerror_r(errno, buff, sizeof(buff));
        g_warning("Failed to open directory %s: %s", path, buff);
    }

}

// get_bss_results
//
// Populate bss files (and directories) list for the given path
// (relative to the directory specified by *dirName"/BSS_results").
//
static void
get_bss_results(const gchar* bss_path)
{
    gchar* top;

    num_displayed_files=0;

    top = g_strjoin(G_DIR_SEPARATOR_S, GETCURDIR, "BSS_results", "", NULL);
    get_results(bss_path, top);
    g_free(top);
}

void
fill_in_bss_results(GtkWidget* widget, ModuleCallbackData* data){
  gchar *text[3];
  int i;

  //set_file_vars();
  get_bss_results(data->bss_path);
  // anne added Apr06 to avoid Gtk WARNINGs when in commandline mode
  if (disp_files_clist == NULL) return; 
  gtk_clist_freeze(GTK_CLIST(disp_files_clist));
  gtk_clist_clear(GTK_CLIST(disp_files_clist));
  for(i=0;i<3;i++) text[i] = (gchar *) malloc(sizeof(char[MAXPATHLEN+1]));
  for(i=0;i<num_displayed_files;i++){
    strcpy(text[0], displayed_files[i].name);
    if (displayed_files[i].size > 0)
        snprintf(text[1], MAXPATHLEN + 1, "%lu", displayed_files[i].size);
    else
        g_strlcpy(text[1], "-", MAXPATHLEN + 1);
    strcpy(text[2], displayed_files[i].date);
    gtk_clist_append(GTK_CLIST(disp_files_clist), text);
  }
  gtk_clist_thaw(GTK_CLIST(disp_files_clist));
  for(i=0;i<3;i++) g_free(text[i]);
  if(bss_files_selected_row!=-1){
    if(bss_files_selected_row>=num_displayed_files){
      bss_files_selected_row=num_displayed_files-1;
    }
    gtk_clist_select_row(GTK_CLIST(disp_files_clist), bss_files_selected_row, 0);
  }
}

void
reset_bss_results(void)
{
    if (cbdata.bss_path != NULL)
        g_free(cbdata.bss_path);
    cbdata.bss_path = g_strdup("");
    fill_in_bss_results(NULL, &cbdata);
}

static void
delete_result_file(int i){
   char cmd_str[MAXPATHLEN+20];

   if(i<0) return;
   sprintf(cmd_str,"rm -rf %s/%s/%s",GETCURDIR,"BSS_results",
           displayed_files[i].name);
   system(cmd_str);
   sprintf(cmd_str,"rm -f %s/%s/%s_noparse",GETCURDIR,"BSS_results",
           displayed_files[i].name);
   system(cmd_str);
}

static void
delete_selected_file(ModuleCallbackData* callback_data,
                     guint callback_action, GtkWidget* widget)
{
  char* name;

  if(bss_files_selected_row==-1){
    printf("Nothing selected!!\n");
    return;
  }
  name = displayed_files[bss_files_selected_row].name;
  if (strcmp("../", &name[strlen(name) - 3]) != 0) {
      if (messQuery("Permanently delete selected file?")) {
          close_results(0,0);
          delete_result_file(bss_files_selected_row);
          fill_in_bss_results(widget, callback_data);
      }
  }
  else
      puts("Directory cannot be deleted!");
}

static void
delete_displayed_files(ModuleCallbackData* callback_data,
                       guint callback_action, GtkWidget* widget)
{
  int i;
  char* name;

  if(!messQuery("Permanently delete all files shown?"))
    return;
  close_results(0,0);
  for(i=0;i<num_displayed_files;i++){
      name = displayed_files[i].name;
      if (strcmp("../", &name[strlen(name) - 3]) != 0)
          delete_result_file(i);
  }
  fill_in_bss_results(widget, callback_data);
}


static void
blast_radiobutton_callback(GtkWidget *widget, gpointer data){
  if (GTK_TOGGLE_BUTTON (widget)->active){
    search_tool_type=BLAST;
    gtk_label_set_text(GTK_LABEL(cutoff_label), "E-val:");
    sprintf(cutoff_str,"%.0Le", blast_eval);
    gtk_entry_set_text(GTK_ENTRY(cutoff_entry), cutoff_str);
    gtk_entry_set_text(GTK_ENTRY(additional_entry), additional_blast);
    //gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
  }
}

static void
megablast_radiobutton_callback(GtkWidget *widget, gpointer data){
  if (GTK_TOGGLE_BUTTON (widget)->active){
    search_tool_type=MEGABLAST;
    gtk_label_set_text(GTK_LABEL(cutoff_label), "E-val:");
    sprintf(cutoff_str,"%.0Le", blast_eval);
    gtk_entry_set_text(GTK_ENTRY(cutoff_entry), cutoff_str);
    gtk_entry_set_text(GTK_ENTRY(additional_entry), additional_megablast);
    //gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
  }
}

static void
blat_radiobutton_callback(GtkWidget *widget, gpointer data){
  if (GTK_TOGGLE_BUTTON (widget)->active){
    search_tool_type=BLAT;
    gtk_label_set_text(GTK_LABEL(cutoff_label), "Score:");
    sprintf(cutoff_str,"%d", blat_score);
    gtk_entry_set_text(GTK_ENTRY(cutoff_entry), cutoff_str);
    gtk_entry_set_text(GTK_ENTRY(additional_entry), additional_blat);
    //gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
  }
}

static void
split_bss_callback(GtkWidget *widget, gpointer data)
{
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
        split_bss_flag = TRUE;
    else
        split_bss_flag = FALSE;
}

static void
cutoff_entry_callback(GtkWidget *widget, gpointer data){
  if(search_tool_type==BLAT)
    sscanf(gtk_entry_get_text(GTK_ENTRY(widget)), "%d", &blat_score);
  else
  {
    sscanf(gtk_entry_get_text(GTK_ENTRY(widget)), "%Le", &blast_eval);

  }
}

static void
additional_entry_callback(GtkWidget *widget, gpointer data){
  if(search_tool_type==BLAST)
    strcpy(additional_blast, gtk_entry_get_text(GTK_ENTRY(widget)));
  else if(search_tool_type==MEGABLAST)
    strcpy(additional_megablast, gtk_entry_get_text(GTK_ENTRY(widget)));
  else if(search_tool_type==BLAT)
    strcpy(additional_blat, gtk_entry_get_text(GTK_ENTRY(widget)));
}

static void
new_subdirectory_entry_callback(GtkWidget* widget, ModuleCallbackData* data)
{
    if (data->new_bss_subdir != NULL) {
        g_free(data->new_bss_subdir);
    }
    data->new_bss_subdir = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
}


static void
query_path_entry_callback(GtkWidget *widget, ModuleCallbackData* data){
  strcpy(query_path, gtk_entry_get_text(GTK_ENTRY(widget)));
  if (data->bss_path != NULL) {
      g_free(data->bss_path);
  }
  data->bss_path = g_strdup("");
}
static void
remove_current_dir(char* path)
{
   char curdir[MAXPATHLEN + 1];
   char *ptr;

   strcpy(curdir, getenv("PWD"));

   ptr = strstr(path, curdir);
   if (ptr != 0) {
     ptr += strlen(curdir);
     strncpy(curdir, ptr, MAXPATHLEN);
     strcpy(path, ".");
     strncat(path, ptr, MAXPATHLEN - strlen(query_path));
   }
}

static void
store_query_path(GtkWidget *widget, StoreQueryPathData* data) {
  gchar *path;
  int len;

  path = (gchar*)gtk_file_selection_get_filename(data->selector);
  strcpy(query_path, path);
  if((len = strlen(query_path)) > 0 && query_path[len-1]=='/'){
      /* Directory selected*/
      strcat(query_path, "*");
  }
  if (data->cbdata->bss_path != NULL) {
      g_free(data->cbdata->bss_path);
  }
  data->cbdata->bss_path = g_strdup("");

  remove_current_dir(query_path);
  gtk_entry_set_text(GTK_ENTRY(query_path_entry), query_path);
}



static void
browse_query_callback(GtkWidget *widget, gpointer data){
  GtkWidget *file_selector;
  const gchar* default_name;

  file_selector=gtk_file_selection_new("Please select a query file or directory");

  default_name = g_strdup_printf("%s/*",GETCURDIR);//.seq";

  gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_selector), default_name);
  gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(file_selector));
  sqpdata.selector = GTK_FILE_SELECTION(file_selector);
  sqpdata.cbdata = data;
  gtk_signal_connect(GTK_OBJECT (GTK_FILE_SELECTION(file_selector)->ok_button),
                     "clicked", GTK_SIGNAL_FUNC (store_query_path), &sqpdata);
  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button),
   		     "clicked", GTK_SIGNAL_FUNC(fill_in_bss_results),data);
  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION(file_selector)->ok_button),
   					 "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
   					 (gpointer) file_selector);
  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION(file_selector)->cancel_button),
   					 "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
   					 (gpointer) file_selector);

  gtk_widget_show(file_selector);
}

static void
db_path_entry_callback(GtkWidget *widget, gpointer data){
  strcpy(db_path, gtk_entry_get_text(GTK_ENTRY(widget)));
}


static void
store_db_path(GtkWidget *widget, gpointer user_data) {
  GtkFileSelection *selector;
  gchar *path;
  int len;

  g_return_if_fail(GTK_IS_FILE_SELECTION(widget));
  selector = GTK_FILE_SELECTION(widget);

  path = (gchar*)gtk_file_selection_get_filename(selector);
  strcpy(db_path, path);
  if((len = strlen(db_path)) > 0 && db_path[len-1]=='/'){
      /* Directory selected*/
      strcat(db_path, "*");
  }
  remove_current_dir(db_path);
  gtk_entry_set_text(GTK_ENTRY(db_path_entry), db_path);
}

static void
browse_db_callback(GtkWidget *widget, gpointer data){
  GtkWidget *file_selector;
  const gchar* default_name;

  file_selector=gtk_file_selection_new("Please select a database file or directory");

  default_name  = g_strdup_printf("%s/*",GETCURDIR);//.fasta";

  gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_selector), default_name);
  gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(file_selector));
  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION(file_selector)->ok_button),
   	              "clicked", GTK_SIGNAL_FUNC (store_db_path), GTK_OBJECT(file_selector));
  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION(file_selector)->ok_button),
   					 "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
   					 (gpointer) file_selector);
  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION(file_selector)->cancel_button),
   					 "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
   					 (gpointer) file_selector);

  gtk_widget_show(file_selector);
}

/* Sets the qryDir, qryFile, dbDir, dbFile variables... compatibility with
 * old BSS code.  Also set qrySuffix, dbSuffix, new variables that allow the
 * user to have sequence files with any suffix.  the 'Dir' variables now
 * contain the full directory path, which allows them to be anywhere on
 * the file system as opposed to in a subdirectory of the fpc file.*/

int set_file_vars()
{
    char *ptr, *ptr2;
    DIR* d;
    FILE* f;

    strcpy(query_path, gtk_entry_get_text(GTK_ENTRY(query_path_entry)));
    strcpy(db_path, gtk_entry_get_text(GTK_ENTRY(db_path_entry)));

    if ( (d=opendir(query_path)))
    {
        /* we were given a directory - take all files*/

        closedir(d);
        if ( (ptr = rindex(query_path,'/')) )
        {
            *ptr = 0;
        }
        strcpy(qryDir,query_path);
        strcpy(qryFile,"<all>");
        strcpy(qrySuffix,"");
    }
    else if ( (f=fopen(query_path,"r")))
    {
        /* we were given a file */

        fclose(f);
        ptr = rindex(query_path,'/');
        if (ptr) *ptr = 0;
        strcpy(qryDir,query_path);
        if (ptr)
        {
            ptr++;
        }
        else
        {
            ptr = query_path;
        }
        strcpy(qryFile,ptr);
        if (strlen(qryDir) == 0)
        {
            strcpy(qryDir,".");
        }
        strcpy(qrySuffix,"");
    }
    else
    {
        /* we expect * or *.ext */

        ptr = rindex(query_path,'*');
        if (ptr == 0)
        {
            printf("No query files specified\n");fflush(0);
            return 0;
        }
        *ptr = 0;
        if ( (d=opendir(query_path)))
        {
            closedir(d);
            if ( (ptr2 = rindex(query_path,'/')) )
            {
                *ptr2 = 0;
            }
            strcpy(qryDir,query_path);
            strcpy(qryFile,"<all>");
            strcpy(qrySuffix,"");   
        }
        else
        {
            printf("Invalid query directory %s\n",query_path);fflush(0);
            return 0;
        }  
        ptr++;
        if (*ptr == '.' && strlen(ptr) > 1)
        {
            ptr++;
            strcpy(qrySuffix,ptr);
        }   
    }
    if ((d=opendir(qryDir)))
    {
        closedir(d);
    }
    else
    {
        printf("Invalid query directory %s\n",qryDir);fflush(0);
        return 0;
    }



    if ( (d=opendir(db_path)))
    {
        /* we were given a directory - take all files*/

        closedir(d);
        if ( (ptr = rindex(db_path,'/')) )
        {
            *ptr = 0;
        }
        strcpy(dbDir,db_path);
        strcpy(dbFile,"<all>");
        strcpy(dbSuffix,"");
    }
    else if ( (f=fopen(db_path,"r")))
    {
        /* we were given a file */

        fclose(f);
        ptr = rindex(db_path,'/');
        if (ptr) *ptr = 0;
        strcpy(dbDir,db_path);
        if (ptr)
        {
            ptr++;
        }
        else
        {
            ptr = db_path;
        }
        strcpy(dbFile,ptr);
        if (strlen(dbDir) == 0)
        {
            strcpy(dbDir,".");
        }
        strcpy(dbSuffix,"");
    }
    else
    {
        /* we expect * or *.ext */

        ptr = rindex(db_path,'*');
        if (ptr == 0)
        {
            printf("No database files specified\n");fflush(0);
            return 0;
        }
        *ptr = 0;
        if ( (d=opendir(db_path)))
        {
            closedir(d);
            if ( (ptr2 = rindex(db_path,'/')) )
            {
                *ptr2 = 0;
            }
            strcpy(dbDir,db_path);
            strcpy(dbFile,"<all>");
            strcpy(dbSuffix,"");   
        }
        else
        {
            printf("Invalid database directory %s\n",db_path);fflush(0);
            return 0;
        }  
        ptr++;
        if (*ptr == '.' && strlen(ptr) > 1)
        {
            ptr++;
            strcpy(dbSuffix,ptr);
        }   
    }
    if ((d=opendir(dbDir)))
    {
        closedir(d);
    }
    else
    {
        printf("Invalid database directory %s\n",dbDir);fflush(0);
        return 0;
    }    


    return 1;
}

static void
start_search_callback(GtkWidget *widget, gpointer data)
{
    if (set_file_vars())
    {
        do_search(ALLCTG,0,0);
    }
}

static void
display_bss_file_callback(GtkWidget *clist, gint row, gint column,
                          GdkEventButton *event, ModuleCallbackData* data)
{
    if(event==NULL) return;
    bss_files_selected_row=row;
    if(event->type==GDK_2BUTTON_PRESS){
        gchar* text;
        guint textlen;
        gtk_clist_get_text(GTK_CLIST(clist), row, 0, &text);
        textlen = strlen(text);
        if (text[textlen - 1] == '/') {  /* split bss directory */
            gchar* c;
            if (textlen >= 3 && strcmp(text + textlen - 3, "../") == 0) {
                data->bss_path[strlen(data->bss_path) - 1] = '\0';
                c = strrchr(data->bss_path, '/');
                if (c != NULL)
                    *(c + 1) = '\0';
                else
                    data->bss_path[0] = '\0';
            }
            else {
                if (data->bss_path != NULL)
                    g_free(data->bss_path);
                data->bss_path = g_strdup(text);
            }
            fill_in_bss_results(clist, data);
        }
        else {
            display_result_file(row);
        }
    }
}

void bss_help(){
   show_help("BSS Help", "BssHelp");
}

void
bss_display(void)
{
  gchar *listtitles[] = {"Output file","File size", "Last modified"};
  GtkWidget *menubar;
  GtkWidget *vbox, *vbox2;
  GtkWidget *hbox;
  GtkWidget *frame;
  GtkWidget *label;
  GtkWidget *button;
  GtkWidget *split_bss_button;
  GtkWidget *blast_radiobutton;
  GtkWidget *megablast_radiobutton;
  GtkWidget *blat_radiobutton;
  GtkWidget *scrolled_window;

  search_tool_type = MEGABLAST;

  assert(BSS_MAXFIELD == BSS_NUMTYPES); // WMN - a bit of a kludj

  if(bss_window!=NULL){
    gdk_window_raise(bss_window->window);
    return;
  }
  memset(&g_bsi,0,sizeof(struct bss_instance));
  bss_files_selected_row=-1;

  //if(query_path[0]=='\0') sprintf(query_path,"%s/*",GETCURDIR);
  *query_path = 0;
  if (cbdata.bss_path != NULL)
      g_free(cbdata.bss_path);
  cbdata.bss_path = g_strdup("");
  if (cbdata.new_bss_subdir != NULL)
      g_free(cbdata.new_bss_subdir);
  cbdata.new_bss_subdir = g_strdup("");

  //if(db_path[0]=='\0') sprintf(db_path,"%s/*",GETCURDIR);
  *db_path = 0;
  bss_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_container_set_border_width (GTK_CONTAINER (bss_window), 0);
  gtk_window_set_title (GTK_WINDOW (bss_window), "FPC BSS");
  gtk_window_set_policy (GTK_WINDOW (bss_window), FALSE, TRUE, FALSE);
  gtk_signal_connect (GTK_OBJECT (bss_window), "destroy",
		      GTK_SIGNAL_FUNC (cancel_button_callback), NULL);

  vbox=gtk_vbox_new(FALSE, 0);
  gtk_container_add (GTK_CONTAINER (bss_window), vbox);

  hbox=gtk_hbox_new(FALSE,1);
  get_main_menu (bss_window, &menubar, &cbdata);
  gtk_box_pack_start (GTK_BOX (hbox), menubar, TRUE, TRUE, 0);
  gtk_widget_show (menubar);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  /* Search files frame*/
  frame = gtk_frame_new("Search files");
  gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  vbox2=gtk_vbox_new(FALSE, 5);
  hbox=gtk_hbox_new(FALSE, 5);
  label = gtk_label_new("Query:");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  query_path_entry = gtk_entry_new_with_max_length(MAXPATHLEN);
  gtk_entry_set_text(GTK_ENTRY(query_path_entry), query_path);
  gtk_signal_connect (GTK_OBJECT (query_path_entry), "changed",
		      GTK_SIGNAL_FUNC (query_path_entry_callback), &cbdata);
  gtk_signal_connect (GTK_OBJECT (query_path_entry), "activate",
		      GTK_SIGNAL_FUNC (fill_in_bss_results), &cbdata);
  gtk_box_pack_start (GTK_BOX (hbox), query_path_entry, TRUE, TRUE, 0);
  button = gtk_button_new_with_label("Browse...");
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (browse_query_callback), &cbdata);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);

  hbox=gtk_hbox_new(FALSE, 5);
  label = gtk_label_new("Database:");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  db_path_entry = gtk_entry_new_with_max_length(MAXPATHLEN);
  gtk_entry_set_text(GTK_ENTRY(db_path_entry), db_path);
  gtk_signal_connect (GTK_OBJECT (db_path_entry), "changed",
		      GTK_SIGNAL_FUNC (db_path_entry_callback), NULL);
  gtk_box_pack_start (GTK_BOX (hbox), db_path_entry, TRUE, TRUE, 0);
  button = gtk_button_new_with_label("Browse...");
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (browse_db_callback), NULL);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);

  gtk_container_add(GTK_CONTAINER(frame), vbox2);

  split_bss_button =
      gtk_check_button_new_with_label("Split BSS output by contig");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(split_bss_button),
                               split_bss_flag);
  gtk_signal_connect(GTK_OBJECT(split_bss_button), "toggled",
                     GTK_SIGNAL_FUNC(split_bss_callback), NULL);

  /* Search tool frame*/
  frame = gtk_frame_new("Search tool");
  gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);

  hbox=gtk_hbox_new(FALSE, 5);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);

  blast_radiobutton = gtk_radio_button_new_with_label(NULL, "BLAST");
  gtk_signal_connect (GTK_OBJECT (blast_radiobutton), "clicked",
                      GTK_SIGNAL_FUNC (blast_radiobutton_callback),
                      split_bss_button);
  gtk_box_pack_start (GTK_BOX (hbox), blast_radiobutton, TRUE, FALSE, 0);

  megablast_radiobutton =
      gtk_radio_button_new_with_label(
          gtk_radio_button_group(GTK_RADIO_BUTTON(blast_radiobutton)),
          "MegaBLAST");
  gtk_signal_connect (GTK_OBJECT(megablast_radiobutton), "clicked",
                      GTK_SIGNAL_FUNC(megablast_radiobutton_callback),
                      split_bss_button);
  gtk_box_pack_start (GTK_BOX (hbox), megablast_radiobutton, TRUE, FALSE, 0);

  blat_radiobutton =
      gtk_radio_button_new_with_label(
          gtk_radio_button_group(GTK_RADIO_BUTTON(megablast_radiobutton)),
          "BLAT");
  gtk_signal_connect (GTK_OBJECT (blat_radiobutton), "clicked",
                      GTK_SIGNAL_FUNC (blat_radiobutton_callback),
                      split_bss_button);
  gtk_box_pack_start (GTK_BOX (hbox), blat_radiobutton, TRUE, FALSE, 0);

  gtk_container_add(GTK_CONTAINER(frame), hbox);

  /* Search parameters frame*/
  frame = gtk_frame_new("Search parameters");
  gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  hbox=gtk_hbox_new(FALSE, 5);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);

  cutoff_label = gtk_label_new("");
  if(search_tool_type==BLAT) gtk_label_set_text(GTK_LABEL(cutoff_label), "Score:");
  else gtk_label_set_text(GTK_LABEL(cutoff_label), "E-val:");
  gtk_box_pack_start (GTK_BOX (hbox), cutoff_label, FALSE, FALSE, 0);

  cutoff_entry=gtk_entry_new_with_max_length(MAX_CUTOFF_LEN);
  gtk_signal_connect(GTK_OBJECT(cutoff_entry), "changed",
		     GTK_SIGNAL_FUNC(cutoff_entry_callback), NULL);
  if(search_tool_type==BLAT) sprintf(cutoff_str,"%d", blat_score);
  else sprintf(cutoff_str,"%.0Le", blast_eval);
  gtk_entry_set_text(GTK_ENTRY(cutoff_entry), cutoff_str);
  gtk_widget_set_usize(cutoff_entry, 62,-2);
  gtk_box_pack_start (GTK_BOX (hbox), cutoff_entry, FALSE, FALSE, 0);

  label = gtk_label_new("Other:");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

  additional_entry=gtk_entry_new_with_max_length(MAX_ADDITIONAL_LEN);
  gtk_signal_connect (GTK_OBJECT (additional_entry), "changed",
		      GTK_SIGNAL_FUNC (additional_entry_callback), NULL);
  if(search_tool_type==BLAST)
    gtk_entry_set_text(GTK_ENTRY(additional_entry), additional_blast);
  else if(search_tool_type==MEGABLAST)
    gtk_entry_set_text(GTK_ENTRY(additional_entry), additional_megablast);
  else if(search_tool_type==BLAT)
    gtk_entry_set_text(GTK_ENTRY(additional_entry), additional_blat);
  gtk_box_pack_start (GTK_BOX (hbox), additional_entry, TRUE, TRUE, 0);

  button = gtk_button_new_with_label("Options...");
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (options_callback), NULL);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);

  gtk_container_add(GTK_CONTAINER(frame), hbox);

  /* Set search tool toggle.  Needs to be here so it can set the eval/score label*/
  if(search_tool_type==BLAST){
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blast_radiobutton), TRUE);
  }
  else if(search_tool_type==MEGABLAST){
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(megablast_radiobutton), TRUE);
  }
  else if(search_tool_type==BLAT){
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blat_radiobutton), TRUE);
  }

  /* BSS output options */
  gtk_container_add(GTK_CONTAINER(vbox), bss_options_frame(split_bss_button));

  /* BSS files suffix*/
  frame = gtk_frame_new("BSS results");
  gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
  vbox2 = gtk_vbox_new(FALSE, 5);
  gtk_container_set_border_width(GTK_CONTAINER (frame), 5);
  gtk_container_add(GTK_CONTAINER(frame), vbox2);
  gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);

  hbox=gtk_hbox_new(FALSE, 5);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);

  /* BSS files clist*/
  scrolled_window=gtk_scrolled_window_new(NULL,NULL);
  gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 5);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  gtk_widget_set_usize(scrolled_window,-2,150);
  disp_files_clist = gtk_clist_new_with_titles(3,listtitles);
  gtk_signal_connect(GTK_OBJECT(disp_files_clist), "select_row",
                     GTK_SIGNAL_FUNC(display_bss_file_callback),
                     &cbdata);
  gtk_clist_set_shadow_type (GTK_CLIST(disp_files_clist), GTK_SHADOW_OUT);
  gtk_clist_set_column_width (GTK_CLIST(disp_files_clist), 0, 175);
  gtk_clist_set_column_auto_resize(GTK_CLIST(disp_files_clist), 1, TRUE);
  gtk_clist_set_column_auto_resize(GTK_CLIST(disp_files_clist), 2, TRUE);
  gtk_clist_set_column_justification(GTK_CLIST(disp_files_clist),1,GTK_JUSTIFY_RIGHT);
  fill_in_bss_results(NULL, &cbdata);
  gtk_container_add(GTK_CONTAINER(scrolled_window),disp_files_clist);
  gtk_container_add(GTK_CONTAINER(vbox2), scrolled_window);

  /* Last buttons row*/
  hbox = gtk_hbox_new(FALSE,5);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
  button = gtk_button_new_with_label("Start search");
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (start_search_callback), NULL);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  button = gtk_button_new_with_label("Close");
  gtk_signal_connect_object(GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT(bss_window));
  gtk_box_pack_end(GTK_BOX (hbox), button, FALSE, FALSE, 0);
  button = gtk_button_new_with_label("Help");
  gtk_signal_connect_object(GTK_OBJECT (button), "clicked",
                      GTK_SIGNAL_FUNC(bss_help), NULL);
  gtk_box_pack_end(GTK_BOX (hbox), button, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  gtk_widget_show_all(bss_window);
}

static GtkWidget*
bss_options_frame(GtkWidget* split_bss_button)
{
    GtkWidget* result;
    GtkWidget* vbox;
    GtkWidget* hbox;

    vbox = gtk_vbox_new(FALSE, 5);
    hbox = gtk_hbox_new(FALSE, 5);

    gtk_container_add(GTK_CONTAINER(vbox),
                      labelled_entry("Subdirectory:", 40,
                                     (GtkSignalFunc)new_subdirectory_entry_callback));
    gtk_box_pack_start(GTK_BOX(hbox), split_bss_button, TRUE, FALSE, 0);

    gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(hbox));

    result = gtk_frame_new("BSS output options");
    gtk_container_set_border_width(GTK_CONTAINER(result), 5);
    gtk_container_add(GTK_CONTAINER(result), vbox);

    return result;
}

static GtkWidget*
labelled_entry(const gchar* text, guint max_length, GtkSignalFunc cb)
{
    GtkWidget* result;
    GtkWidget* entry;
    GtkWidget* label;

    result = gtk_hbox_new(FALSE, 0);
    label = gtk_label_new(text);
    entry = gtk_entry_new_with_max_length(max_length);
    gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(cb),
                       &cbdata);
    gtk_widget_set_usize(entry, 80, -2);
    gtk_box_pack_start(GTK_BOX(result), label, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(result), entry, TRUE, TRUE, 2);
    return result;
}

const gchar*
new_bss_subdir(void)
{
    if (cbdata.new_bss_subdir == NULL)
        cbdata.new_bss_subdir = g_strdup("");
    return cbdata.new_bss_subdir;
}
